package cool.taomu.toolkit.retry

import com.google.common.util.concurrent.RateLimiter
import java.lang.reflect.Method
import java.util.concurrent.CompletableFuture
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicInteger
import org.aopalliance.intercept.MethodInterceptor
import org.aopalliance.intercept.MethodInvocation
import java.util.concurrent.TimeUnit
import org.slf4j.LoggerFactory
import com.google.common.base.Optional

class RetryInterceptor implements MethodInterceptor {
    var static LOG = LoggerFactory.getLogger(RetryInterceptor);
    var scheduler = Executors.newScheduledThreadPool(2);

    override invoke(MethodInvocation invocation) throws Throwable {
        var Method method = invocation.getMethod();
        var retry = method.getAnnotation(Retry);

        // 如果方法没有 @Retry 注解，直接执行
        if (retry === null) {
            return invocation.proceed();
        }

        var int maxAttempts = retry.maxAttempts();
        var long delay = retry.delay();
        var boolean exponential = retry.exponentialBackoff();
        var double rateLimit = retry.rateLimit();

        var rateLimiter = rateLimit > 0 ? RateLimiter.create(rateLimit) : null;
        // 使用 CompletableFuture 实现异步
        var CompletableFuture<Object> future = new CompletableFuture();

        var AtomicInteger attempt = new AtomicInteger(1);

        retry(invocation, maxAttempts, delay, exponential, rateLimiter, future, attempt);

        return future;
    }

    private def void retry(MethodInvocation invocation, int maxAttempts, long delay, boolean exponential,
        RateLimiter rateLimiter, CompletableFuture<Object> future, AtomicInteger attempt) {

        if (attempt.get() > maxAttempts) {
            var String msg = "重试失败，超过最大尝试次数: " + maxAttempts;
            LOG.error(msg);
            future.completeExceptionally(new RuntimeException(msg));
            return;
        }

        scheduler.execute([
            try {
                if(rateLimiter !== null) rateLimiter.acquire();
                var Object result = invocation.proceed();
                LOG.info("方法调用成功 [{}], 重试次数: {}", invocation.getMethod(), attempt.get() - 1);

                LOG.info("调用 future.complete()");
                // logger.warn("调用 future.completeExceptionally()");
                var cr = result as CompletableFuture<?>
                var result2 = cr.get();
                if (result2 instanceof Optional) {
                    if (result2.present) {
                        LOG.debug("{} 返回值 : {}", invocation.method.name, result2.get())
                    }
                } else {
                    LOG.debug("{} 返回值 : {}", invocation.method.name, result2)
                }
                future.complete(result2);
            } catch (Throwable t) {
                var int currentAttempt = attempt.incrementAndGet();
                LOG.warn("方法调用失败 [{}], 第 {} 次重试, 原因: {}", invocation.getMethod(), currentAttempt - 1, t.getMessage(),
                    t);
                scheduleRetry(invocation, maxAttempts, delay, exponential, rateLimiter, future, attempt, currentAttempt,
                    t);
            }
        ]);
    }

    private def void scheduleRetry(MethodInvocation invocation, int maxAttempts, long delay, boolean exponential,
        RateLimiter rateLimiter, CompletableFuture<Object> future, AtomicInteger attempt, int currentAttempt,
        Throwable t) {
        var long actualDelay = exponential ? Math.pow(2, currentAttempt) as long * delay : delay;
        LOG.info("将在 {} 毫秒后进行第 {} 次重试 [方法: {}]", actualDelay, currentAttempt, invocation.getMethod());
        scheduler.schedule([
            retry(invocation, maxAttempts, delay, exponential, rateLimiter, future, attempt);
        ], actualDelay, TimeUnit.MILLISECONDS);
    }

}
